/// Text Overlay Application: Main Class
/// This class contains the entry point of the application and all the 
/// functions requiered to Render data on the screen.


using System;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Data;
using System.Text.RegularExpressions;
using System.IO;
using System.Text;
using System.Data.SqlClient;

using System.Net.NetworkInformation;
using System.Diagnostics;
using System.Runtime.InteropServices;
using CommLayer;
using Tamir.IPLib;
using Tamir.IPLib.Packets;
using Magnolia;

namespace TextOverlay
{
    /// <summary>TextOverlay Class</summary>
    public partial class TextOverlay : IFrameworkCallback, IDeviceCreation
    {
        #region Creation
        /// <summary>Create a new instance of the class</summary>
        public TextOverlay(Framework f)
        {
            // Store framework
            sampleFramework = f;
            // Create dialogs
            hud = new Dialog(sampleFramework);
            sampleUi = new Dialog(sampleFramework);
        }
        #endregion

        private bool fullmode = true;       // indicates if the application runs in full mode or preview mode 
        private bool fullscreen = true;    // indicates if the application runs in full screen or windowed mode
        private bool localtest = false;      // indicates if the application runs as a local test

        private UDPBroadcast UDP = new UDPBroadcast("127.0.0.1");
        // Variables
        private Framework sampleFramework = null; // Framework for samples
        private Font statsFont = null; // Font for drawing text
        private DrawCC CloseCaption1;
        private DrawCC CloseCaption2;

        private String LocalHost = "127.0.0.1";

        private CMonitor Monitor = new CMonitor();
        private CExpressions Expression = new CExpressions(); // Regular Expression and Common Functions
        private CXMLParser SAX = new CXMLParser(1);
        private Sprite textSprite = null; // Sprite for batching text calls
        private Effect effect = null; // D3DX Effect Interface
        private ModelViewerCamera camera = new ModelViewerCamera(); // A model viewing camera
        private bool isHelpShowing = true; // If true, renders the UI help text
        private Dialog hud = null; // dialog for standard controls
        private Dialog sampleUi = null; // dialog for sample specific controls
        private CTools Tools = new CTools();
        public static Stopwatch Watch1 = new Stopwatch();
        public static Stopwatch Watch2 = new Stopwatch();
        public static Stopwatch Watch3 = new Stopwatch();

        private static DateTime EndClock = DateTime.Parse("2013/10/28 16:57:00");

        private string AppPath = System.Windows.Forms.Application.StartupPath;
        public DateTime LastFrameRenderedAt = DateTime.Now;

        // HUD Ui Control constants
        private const int ToggleFullscreen = 1;
        private const int ToggleReference = 3;
        private const int ChangeDevice = 4;
        private const int Yoffset = 10;



        static void ThreadExceptionHandler(Object sender, System.Threading.ThreadExceptionEventArgs e)
        {
            Track.LogInfo("~~Otherwise Unhandled Thread Exception Caught: " + e.Exception.Message + Environment.NewLine + e.Exception.StackTrace);
        }

        static public int close_overlay_application = 0;

        static void GenericHandler(Object Sender, UnhandledExceptionEventArgs Args)
        {
            if (TextOverlay.close_overlay_application == 1)
            {
                Track.LogInfo("CLOSE THIS APPLICATION!");
                Environment.Exit(1);
            }
            else
            {
                Exception e = (Exception)Args.ExceptionObject;
                Track.LogInfo("~~Otherwise Unhandled Generic Exception Caught: " + e.Message + Environment.NewLine + e.StackTrace);
            }
        }


        /// <summary>
        /// Called during device initialization, this code checks the device for some 
        /// minimum set of capabilities, and rejects those that don't pass by returning false.
        /// </summary>
        public bool IsDeviceAcceptable(Caps caps, Format adapterFormat, Format backBufferFormat, bool windowed)
        {
            // Skip back buffer formats that don't support alpha blending
            if (!Manager.CheckDeviceFormat(caps.AdapterOrdinal, caps.DeviceType, adapterFormat,
                Usage.QueryPostPixelShaderBlending, ResourceType.Textures, backBufferFormat))
                return false;

            return true;
        }

        static void Application_CLOSED(object sender,EventArgs e)
        {
            Track.LogInfo("Use Application_CLOSED");
            Environment.Exit(1);
        }

        /// <summary>
        /// Entry point to the program. Initializes everything and goes into a message processing 
        /// loop. Idle time is used to render the scene.
        /// There are two modes to initialize the application.
        /// FullMode = Initializes the application in full screen mode (fullmode = true)
        /// MiniMode = Initializes the application in preview mode (fullmode = false)
        /// </summary>
        static int Main(string[] arg)
        {
            //Debug.Assert(false, "Error: Debug Version");
            System.Windows.Forms.Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(ThreadExceptionHandler);
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(GenericHandler);

            // Check if there is another instance of the application running
            // if more than one instance of the application running, shut it down displaying
            // a warning message
            //int AppCounter = 0;
            Process[] AP = Process.GetProcesses();
            foreach (Process p in AP)
            {
                if (p.ProcessName.Contains("TextOverlay.vshost"))
                { }
                else if (p.ProcessName.Contains("TextOverlay") && (p.StartTime.AddSeconds(5) < DateTime.Now))
                {
                    p.Kill();
                }
                p.Exited += new EventHandler(TextOverlay.Application_CLOSED);

            }
            //Process[] AP = Process.GetProcesses();
            //for (int a = 0; a < AP.Length; a++)
            //{
            //    if (AP[a].ProcessName.Contains("TextOverlay") == true)
            //        AppCounter++;
            //}

            //if (AppCounter > 1)
            //{
            //    System.Windows.Forms.MessageBox.Show("Another instance of this application is running\nplease close all other instances of this application and restart");
            //    return 0;
            //}
            string lastCall = "Creating Track Log";
            Track.CreateNewLog();
            //arg = new string[1];
            //arg[0] = @"D:\Documents and Settings\Sebastian\Desktop\text\TemplatePreview.sstext";
            try
            {
                lastCall = "Collecting Arguments";
                StringBuilder Arguments = new StringBuilder();
                if (arg.Length > 0)
                {
                    for (int x = 0; x < arg.Length; x++)
                        Arguments.Append(arg[x] + " ");
                }


                
                lastCall = "Creating FrameWork";
                System.Windows.Forms.Application.EnableVisualStyles();
                using (Framework sampleFramework = new Framework())
                {

                    lastCall = "Creating Aux Device";
                    TextOverlay sample = new TextOverlay(sampleFramework);
                    sample.CreateAuxDevice();
                    

                    lastCall = "Setting CallBack functions";
                    // Set the callback functions. These functions allow the sample framework to notify
                    // the application about device changes, user input, and windows messages.  The 
                    // callbacks are optional so you need only set callbacks for events you're interested 
                    // in. However, if you don't handle the device reset/lost callbacks then the sample 
                    // framework won't be able to reset your device since the application must first 
                    // release all device resources before resetting.  Likewise, if you don't handle the 
                    // device created/destroyed callbacks then the sample framework won't be able to 
                    // recreate your device resources.
                    sampleFramework.Disposing += new EventHandler(sample.OnDestroyDevice);
                    sampleFramework.DeviceLost += new EventHandler(sample.OnLostDevice);
                    sampleFramework.DeviceCreated += new DeviceEventHandler(sample.OnCreateDevice);
                    sampleFramework.DeviceReset += new DeviceEventHandler(sample.OnResetDevice);
                    sampleFramework.SetWndProcCallback(new WndProcCallback(sample.OnMsgProc));


                    sampleFramework.SetCallbackInterface(sample);
                    if (sample.fullmode != true)
                    {
                        lastCall = "Initializing Application in Preview Mode";
                        // Show the cursor and clip it when in full screen
                        sampleFramework.SetCursorSettings(true, false);
                        // Initialize
                        sample.InitializeApplication();
                        sample.ScaleFactor = 2;
                        // Initialize the sample framework and create the desired window and Direct3D 
                        // device for the application. Calling each of these functions is optional, but they
                        // allow you to set several options which control the behavior of the sampleFramework.
                        lastCall = "Creating Window";
                        sampleFramework.Initialize(true, true, true); // Parse the command line, handle the default hotkeys, and show msgboxes


                        sampleFramework.CreateWindow("TextOverlay");
                        // Hook the keyboard event
                        lastCall = "Creating Handler for Keyboard Events";
                        sampleFramework.Window.KeyDown += new System.Windows.Forms.KeyEventHandler(sample.OnKeyEvent);
                        lastCall = "Creating Direct3D Device";
                        sampleFramework.CreateDevice(0, true, Framework.DefaultSizeWidth, Framework.DefaultSizeHeight, sample);
                        lastCall = "Retreive Arguments";
                        sample.UpdateData(Arguments.ToString());

                        //sample.ImagesReady = true;
                        //sample.UpdateData(sample.AppPath + "\\XML\\images.xml");
                        
                        sample.UpdateData(sample.AppPath + "\\XML\\config mini.xml");
                        sample.ProcessOneUpdate();
                        sample.MiniLoad();
                        sample.thisFrame = 0;
                    }
                    else
                    {
                        lastCall = "Set Cursor Settings";
                        // Show the cursor and clip it when in full screen
                        if (sample.localtest == false)
                            sampleFramework.SetCursorSettings(false, true);

                        // Initialize
                        lastCall = "Initializing Application";
                        sample.InitializeApplication();

                        lastCall = "Initialize Framework";

                        // Initialize the sample framework and create the desired window and Direct3D 
                        // device for the application. Calling each of these functions is optional, but they
                        // allow you to set several options which control the behavior of the sampleFramework.
                        sampleFramework.Initialize(false, true, false); // Parse the command line, handle the default hotkeys, and show msgboxes

                        lastCall = "Creating Window";
                        sampleFramework.CreateWindow("TextOverlay");

                        // Hook the keyboard event
                        sampleFramework.Window.KeyDown += new System.Windows.Forms.KeyEventHandler(sample.OnKeyEvent);
                        lastCall = "Creating Window";
                        sampleFramework.CreateDevice(0, true, Framework.DefaultSizeWidth, Framework.DefaultSizeHeight, sample);

                        if (sample.fullscreen == true)
                        {
                            lastCall = "Toggle Fullscreen";
                            sampleFramework.ToggleFullscreen();
                        }

                        AppContext.AppTraceSwitch = new TraceSwitch("Magnolia", "Magnolia trace switch");

                        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
                        Thread.CurrentThread.Priority = ThreadPriority.Highest;
                        Thread.Sleep(0);

                        // Start Data Server and Sync Server
                        sample.InitializeServers();

                        // Start ProcessUpdates Thread
                        Thread TimelineUpdateThread = new Thread(new ThreadStart(sample.TimelineUpdateLoop));
                        TimelineUpdateThread.Name = "TimelineUpdateThread";
                        TimelineUpdateThread.Start();

                        // Start ReportStatus Thread
                        Thread ReportStatusThread = new Thread(new ThreadStart(sample.ReportStatus));
                        ReportStatusThread.Name = "ReportStatusThread";
                        ReportStatusThread.Start();

                        sample.ScaleFactor = 2;
                    }

                    // Pass control to the sample framework for handling the message pump and 
                    // dispatching render calls. The sample framework will call your FrameMove 
                    // and FrameRender callback when there is idle time between handling window messages.
                    sampleFramework.MainLoop();
                    sample.Close();
                    return sampleFramework.ExitCode;
                }
            }
            catch (Exception e)
            {
                //System.Windows.Forms.MessageBox.Show(e.Message, "Error Starting DirectX Framework");
                //System.Windows.Forms.MessageBox.Show(lastCall, "LastCall");
                return 0;
            }

        }

        static string GetImageTagXML(string fileName)
        {
            SqlConnection myConnection;
            SqlCommand myCommand;
            SqlDataAdapter myAdapter;
            DataSet ds = new DataSet();
            string connectionString = "Data Source=SQL01;Initial Catalog=SuperSign;Persist Security Info=True;User ID=sa; Password=sa";

            myConnection = new SqlConnection(connectionString);
            myCommand = new SqlCommand();
            myCommand.Connection = myConnection;
            myCommand.CommandText = "Select * from ImageTag";

            StreamReader textReader = new StreamReader(fileName);
            string line = textReader.ReadToEnd();
            textReader.Close();
            string startString = "IMAGETAG ID=\"";
            string endString = "\"";
            string pattern = startString + "(.*?)" + endString;

            Match mc = Regex.Match(line, pattern);
            ArrayList imageList = new ArrayList();

            while (mc.Success)
            {
                string test = mc.ToString();
                int newID = Convert.ToInt32(mc.ToString().Substring(13).TrimEnd('\"'));
                if(!imageList.Contains(newID))
                    imageList.Add(newID);
                mc= mc.NextMatch();
            }

            try
            {
                myConnection.Open();
                Track.LogInfo("DB CONNECTION OK");
                myAdapter = new SqlDataAdapter(myCommand);
                myAdapter.Fill(ds, "IMAGETAG");
            }
            catch (Exception err)
            {
                Track.LogInfo("DB CONNECTION FAIL!");
            }
            finally
            {
                myConnection.Close();
            }

            string xmlString = "<ImageTags>";
            foreach (DataRow dr in ds.Tables["IMAGETAG"].Rows)
            {
                if (imageList.Contains(Convert.ToInt32(dr["ID"].ToString())))
                {
                    xmlString += "<IMAGETAG ID=\"" + dr["ID"].ToString() + "\" ";
                    xmlString += "Name=\"" + dr["Name"].ToString() + "\" ";
                    xmlString += "ImagePath=\"" + dr["ImagePath"].ToString().Replace("Y:/", @"\\Web01\UploadImage\") + "\" ";
                    xmlString += "LastUpdated=\"" + dr["LastUpdated"].ToString() + "\"/>";
                }
            }
            xmlString += "</ImageTags>";

            return xmlString;
        }

        /// <summary>
        /// Closed the Application.
        /// This function is called once the Main loop has been exited. It closed the processing thread
        /// and creates varios reports of activity and performance.
        /// </summary>
        public void Close()
        {
            if (fullmode == true)
            {
                KillBackThread = true;

                if (TimeDump.Count > 0)
                    Tools.ReportAsCSV("Sync Detail", TimeDump);

                Track.LogInfo("Closing Application");
                // Close Data and Sync Servers                         
                if (m_CommData != null)
                {
                    Track.LogInfo("Closing m_CommData");
                    m_CommData.Close();
                }
                if (m_CommSync != null)
                {
                    Track.LogInfo("Closing m_CommSyn");
                    m_CommSync.Close();
                }

            }

        }

        private bool KillBackThread = false;

        /// <summary>
        /// Creates a loop that calls the ProcessOneUpdate() function and then sleeps for 5 milliseconds.
        /// This function is also the entry point of the TimelineUpdateThread.
        /// </summary>
        public void TimelineUpdateLoop()
        {
            Thread.CurrentThread.Priority = ThreadPriority.Lowest;
            Thread.Sleep(0);
            ProcessOneUpdate();
            
            if (localtest == true)
            {
                ImagesReady = true;
                UpdateData(AppPath + "\\XML\\config mitsu.xml");
                ProcessOneUpdate();
                while (KillBackThread == false)
                {
                    UpdateData(AppPath + "\\XML\\timeline mitsu.xml");
                    ProcessOneUpdate();
                    Thread.Sleep(5000);
                }
            }
            else
            {
                while (KillBackThread == false)
                {
                    ProcessOneUpdate();
                    Thread.Sleep(10);
                }
            }
            Thread.CurrentThread.Abort();
        }


        /// <summary>
        /// This callback function is called immediately before a device is created to allow the 
        /// application to modify the device settings. The supplied settings parameter 
        /// contains the settings that the framework has selected for the new device, and the 
        /// application can make any desired changes directly to this structure.  Note however that 
        /// the sample framework will not correct invalid device settings so care must be taken 
        /// to return valid device settings, otherwise creating the Device will fail.  
        /// </summary>
        public void ModifyDeviceSettings(DeviceSettings settings, Caps caps)
        {
            // If device doesn't support HW T&L or doesn't support 1.1 vertex shaders in HW 
            // then switch to SWVP.
            if ((!caps.DeviceCaps.SupportsHardwareTransformAndLight) ||
                (caps.VertexShaderVersion < new System.Version(1, 1)))
            {
                settings.BehaviorFlags = CreateFlags.SoftwareVertexProcessing;
            }
            else
            {
                settings.BehaviorFlags = CreateFlags.HardwareVertexProcessing;
            }

            // This application is designed to work on a pure device by not using 
            // any get methods, so create a pure device if supported and using HWVP.
            if ((caps.DeviceCaps.SupportsPureDevice) &&
                ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing) != 0))
                settings.BehaviorFlags |= CreateFlags.PureDevice;

            // Debugging vertex shaders requires either REF or software vertex processing 
            // and debugging pixel shaders requires REF.  
#if(DEBUG_VS)
            if (settings.DeviceType != DeviceType.Reference )
            {
                settings.BehaviorFlags &= ~CreateFlags.HardwareVertexProcessing;
                settings.BehaviorFlags |= CreateFlags.SoftwareVertexProcessing;
            }
#endif
#if(DEBUG_PS)
            settings.DeviceType = DeviceType.Reference;
#endif
            // For the first device created if its a REF device, optionally display a warning dialog box
            if (settings.DeviceType == DeviceType.Reference)
            {
                Utility.DisplaySwitchingToRefWarning(sampleFramework, "TextOverlay");
            }

        }



        /// <summary>
        /// This event will be fired immediately after the Direct3D device has been 
        /// created, which will happen during application initialization and windowed/full screen 
        /// toggles. This is the best location to create Pool.Managed resources since these 
        /// resources need to be reloaded whenever the device is destroyed. Resources created  
        /// here should be released in the Disposing event. 
        /// </summary>
        private void OnCreateDevice(object sender, DeviceEventArgs e)
        {
            d3d = e.Device;
            // Initialize the stats font
            if (fullmode == true)
            {
                statsFont = ResourceCache.GetGlobalInstance().CreateFont(e.Device, 16, 0, FontWeight.Normal, 1, false, CharacterSet.Default,
                    Precision.Outline, FontQuality.Default, PitchAndFamily.FamilyDoNotCare | PitchAndFamily.DefaultPitch
                    , "Arial");

            }
            else
            {
                statsFont = ResourceCache.GetGlobalInstance().CreateFont(e.Device, 14, 0, FontWeight.Bold, 1, false, CharacterSet.Default,
                    Precision.Default, FontQuality.Default, PitchAndFamily.FamilyDoNotCare | PitchAndFamily.DefaultPitch
                    , "Verdana");
            }

            CloseCaption1 = new DrawCC(e.Device, 3);
            CloseCaption2 = new DrawCC(e.Device, 3);


            // Define DEBUG_VS and/or DEBUG_PS to debug vertex and/or pixel shaders with the 
            // shader debugger. Debugging vertex shaders requires either REF or software vertex 
            // processing, and debugging pixel shaders requires REF.  The 
            // ShaderFlags.Force*SoftwareNoOptimizations flag improves the debug experience in the 
            // shader debugger.  It enables source level debugging, prevents instruction 
            // reordering, prevents dead code elimination, and forces the compiler to compile 
            // against the next higher available software target, which ensures that the 
            // unoptimized shaders do not exceed the shader model limitations.  Setting these 
            // flags will cause slower rendering since the shaders will be unoptimized and 
            // forced into software.  See the DirectX documentation for more information about 
            // using the shader debugger.
            ShaderFlags shaderFlags = ShaderFlags.NotCloneable;

            // Read the D3DX effect file
            try
            {
                string path = Utility.FindMediaFile("TOVGui.fx");
                effect = ResourceCache.GetGlobalInstance().CreateEffectFromFile(e.Device,
                    path, null, null, shaderFlags, null);
            }
            catch (Exception ex)
            {
                //System.Windows.Forms.MessageBox.Show(ex.Message, "Error Loading TOVGui.fx");
            }
            // Setup the camera's view parameters
            camera.SetViewParameters(new Vector3(0.0f, 0.0f, -1.0f), Vector3.Empty);

        }

        private Texture MiniBack;
        private Texture MiniDefault;
        private Sprite MiniSprite;
        private Texture MiniBoxes, BlackBox, MiniWarning, MiniBar, MiniPlay, MiniStop, MiniPause, MiniPlaying, MiniRew, MiniFF, MiniSave, MiniCamera, MiniPrev, MiniNext;
        /// <summary>
        /// This event will be fired immediately after the Direct3D device has been 
        /// reset, which will happen after a lost device scenario. This is the best location to 
        /// create Pool.Default resources since these resources need to be reloaded whenever 
        /// the device is lost. Resources created here should be released in the OnLostDevice 
        /// event. 
        /// </summary>
        private void OnResetDevice(object sender, DeviceEventArgs e)
        {
            Track.LogInfo("Reseting Direct3D Device");
            SurfaceDescription desc = e.BackBufferDescription;
            // Create a sprite to help batch calls when drawing many lines of text
            if (textSprite == null)
            {
                Console.WriteLine("Reseting TextSprite");
                textSprite = new Sprite(e.Device);
            }

            if (MiniSprite == null)
            {
                Console.WriteLine("Reseting MiniSprite");
                MiniSprite = new Sprite(e.Device);
            }

            if (BlackSprite == null)
            {
                Console.WriteLine("Reseting BlackSprite");
                BlackSprite = new Sprite(e.Device);
            }

            if (BlackTexture == null)
            {
                Console.WriteLine("Reseting BlackTexture");
                BlackTexture = TextureLoader.FromFile(d3d, Utility.FindMediaFile("black.jpg")); // new Texture(e.Device, 1, 1, 1, Usage.SoftwareProcessing, Format.X8R8G8B8, Pool.Default);
            }

            if (BlackBox == null)
            {
                Console.WriteLine("Reseting BlackBox");
                BlackBox = TextureLoader.FromFile(d3d, Utility.FindMediaFile("BlackBox.jpg"));
            }

            if (fullmode != true)
            {
                UpdateData(AppPath + "\\XML\\config mini.xml");              
                MiniBack = TextureLoader.FromFile(d3d, Utility.FindMediaFile("miniback.jpg"));
                //MiniDefault = TextureLoader.FromFile(d3d, Utility.FindMediaFile("ABC News COLOR Logo 80 Over Black_250.jpg"));
                MiniBar = TextureLoader.FromFile(d3d, Utility.FindMediaFile("minibar.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniPlay = TextureLoader.FromFile(d3d, Utility.FindMediaFile("miniplay.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniStop = TextureLoader.FromFile(d3d, Utility.FindMediaFile("ministop.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniPause = TextureLoader.FromFile(d3d, Utility.FindMediaFile("minipause.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniPlaying = TextureLoader.FromFile(d3d, Utility.FindMediaFile("miniplaying.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniRew = TextureLoader.FromFile(d3d, Utility.FindMediaFile("minirew.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniFF = TextureLoader.FromFile(d3d, Utility.FindMediaFile("miniff.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniSave = TextureLoader.FromFile(d3d, Utility.FindMediaFile("minisave.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniCamera = TextureLoader.FromFile(d3d, Utility.FindMediaFile("minicamera.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniNext = TextureLoader.FromFile(d3d, Utility.FindMediaFile("mininext.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniPrev = TextureLoader.FromFile(d3d, Utility.FindMediaFile("miniprev.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                MiniWarning = TextureLoader.FromFile(d3d, Utility.FindMediaFile("miniwarning.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
                //MiniBoxes = TextureLoader.FromFile(d3d, Utility.FindMediaFile("boxes.png"), 0, 0, 1, Usage.None, Format.Unknown, Pool.Default, Filter.None, Filter.None, System.Drawing.Color.Magenta.ToArgb());
            }
            else
            {
                Console.WriteLine("Reseting Displays");
                if (Display != null)
                {
                    for (int x = 0; x < Display.Length; x++)
                    {
                        Display[x].m_Texture = new Texture(d3d, Convert.ToInt32(Display[x].Width), Convert.ToInt32(Display[x].Height), 1, Usage.RenderTarget, Format.X8R8G8B8, Pool.Default);
                        Display[x].m_Surface = Display[x].m_Texture.GetSurfaceLevel(0);
                        Display[x].m_Sprite = new Sprite(d3d);
                    }
                    if (SurfaceHelper == null)
                        SurfaceHelper = new RenderToSurface(d3d, Convert.ToInt32(Display[0].Width), Convert.ToInt32(Display[0].Height), Format.X8R8G8B8, true, DepthFormat.D16);
                    if (mSprite == null)
                        mSprite = new Sprite(d3d);
                    ClearDisplay();
                }
            }

            // Setup the camera's projection parameters
            float aspectRatio = (float)desc.Width / (float)desc.Height;
            camera.SetProjectionParameters((float)Math.PI / 4, aspectRatio, 0.1f, 10.0f);
            camera.SetWindow(desc.Width, desc.Height);

            // Setup UI locations
            hud.SetLocation(desc.Width - 170, 0);
            hud.SetSize(170, 170);
            sampleUi.SetLocation(desc.Width - 170, desc.Height - 350);
            sampleUi.SetSize(170, 300);

        }


        /// <summary>
        /// This event function will be called fired after the Direct3D device has 
        /// entered a lost state and before Device.Reset() is called. Resources created
        /// in the OnResetDevice callback should be released here, which generally includes all 
        /// Pool.Default resources. See the "Lost Devices" section of the documentation for 
        /// information about lost devices.
        /// </summary>
        private void OnLostDevice(object sender, EventArgs e)
        {
            Track.LogInfo("Direct3D device has been lost!");
            Track.LogInfo("Starting OnLostDevice() Procedures");
            try
            {
                if (MiniSprite != null)
                {
                    MiniSprite.Dispose();
                    MiniSprite = null;
                }

                if (textSprite != null)
                {
                    textSprite.Dispose();
                    textSprite = null;
                }

                if (mSprite != null)
                {
                    mSprite.Dispose();
                    mSprite = null;
                }

                if (SurfaceHelper != null)
                {
                    SurfaceHelper.Dispose();
                    SurfaceHelper = null;
                }

                if (BlackTexture != null)
                {
                    BlackTexture.Dispose();
                    BlackTexture = null;
                }

                if (BlackBox != null)
                {
                    BlackBox.Dispose();
                    BlackBox = null;
                }

                if (BlackSprite != null)
                {
                    BlackSprite.Dispose();
                    BlackSprite = null;
                }

                if (Display != null)
                    for (int x = 0; x < Display.Length; x++)
                    {
                        if (Display[x].m_Texture != null)
                        {
                            Display[x].m_Texture.Dispose();
                            Display[x].m_Texture = null;
                            Display[x].m_Surface.Dispose();
                            Display[x].m_Surface = null;
                            Display[x].m_Sprite.Dispose();
                            Display[x].m_Sprite = null;
                        }
                    }

                if (fullmode != true)
                {

                    MiniSprite = null;
                    if(MiniBack != null)
                        MiniBack.Dispose();
                    MiniBack = null;
                    if (MiniDefault != null)
                        MiniDefault.Dispose();
                    MiniDefault = null;
                    if (MiniBar != null)
                        MiniBar.Dispose();
                    MiniBar = null;
                    if (MiniPlay != null)
                        MiniPlay.Dispose();
                    MiniPlay = null;
                    if (MiniStop != null)
                        MiniStop.Dispose();
                    MiniStop = null;
                    if (MiniPause != null)
                        MiniPause.Dispose();
                    MiniPause = null;
                    if (MiniPlaying != null)
                        MiniPlaying.Dispose();
                    MiniPlaying = null;
                    if (MiniRew != null)
                        MiniRew.Dispose();
                    MiniRew = null;
                    if (MiniFF != null)
                        MiniFF.Dispose();
                    MiniFF = null;
                    if (MiniSave != null)
                        MiniSave.Dispose();
                    MiniSave = null;
                    if (MiniNext != null)
                        MiniNext.Dispose();
                    MiniPrev.Dispose();
                    if (MiniCamera != null)
                        MiniCamera.Dispose();
                    MiniCamera = null;
                    if (MiniWarning != null)
                        MiniWarning.Dispose();
                    MiniWarning = null;
                    if (MiniBoxes != null)
                        MiniBoxes.Dispose();
                    MiniBoxes = null;
                }
            }
            catch (Exception ex)
            {
                Track.LogInfo("Failure Attempting OnLostDevice() Procedures");
                Track.LogInfo(ex.Message);
            }
        }

        /// <summary>
        /// This callback function will be called immediately after the Direct3D device has 
        /// been destroyed, which generally happens as a result of application termination or 
        /// windowed/full screen toggles. Resources created in the OnCreateDevice callback 
        /// should be released here, which generally includes all Pool.Managed resources. 
        /// </summary>
        private void OnDestroyDevice(object sender, EventArgs e)
        {
            Track.LogInfo("Direct3D device has been destroyed!");
            Track.LogInfo("Starting OnDestroyDevice() Procedures");
            //
            close_overlay_application = 1;
            System.Environment.Exit(1);
        }

        /// <summary>
        /// This callback function will be called once at the beginning of every frame. This is the
        /// best location for your application to handle updates to the scene, but is not 
        /// intended to contain actual rendering calls, which should instead be placed in the 
        /// OnFrameRender callback.  
        /// </summary>
        public void OnFrameMove(Device device, double appTime, float elapsedTime)
        {
            // Nothing
        }

        /// <summary>
        /// This function loads the requested .sstext file into de application
        /// the application looks every 60 frames (1 second) into the \\Temp folder 
        /// for a file temp._sstext, if it finds one, loads it and then delete it.
        /// </summary>
        public void MiniLoad()
        {
            Thread TimelineUpdateThread = new Thread(new ThreadStart(TimelineUpdateLoop));
            TimelineUpdateThread.Name = "MiniTimelineUpdateThread";
            TimelineUpdateThread.Start();

            if (File.Exists(AppPath + "\\Temp\\temp._sstext"))
            {
                UpdateData(GetImageTagXML(AppPath + "\\Temp\\temp._sstext"));
                lock (lock_RenderFunction)
                {
                    for (int x = 0; x < Timeline.Length; x++)
                    {
                        if (Timeline[x].DXComboLib.Count > 0)
                            Timeline[x].DXComboLib.Clear();
                    }
                    UpdateData(AppPath + "\\Temp\\temp._sstext");


                    File.Delete(AppPath + "\\Temp\\temp._sstext");
                    thisFrame = 0;
                }
            }
            MiniLoadCounter = 0;
        }


        private int MiniState = 0;
        private bool SaveFrame = false;
        private bool ShowCamera = false;
        private int MiniLoadCounter = 0;
        private string thisCombo;
        private int thisDuration;
        /// <summary>
        /// This is version of the Render Function with the necessary changes to make the
        /// application run in Preview Mode
        /// </summary>
        /// <param name="device"></param>
        public void MiniMe(Device device)
        {
            try
            {
                if (MiniLoadCounter > 60)
                    MiniLoad();
                else
                    MiniLoadCounter++;

                lock (lock_RenderFunction)
                {
                    // Check for heavy operations in queue to be created
                    UpdateApplication();

                    // Clear All Display Areas
                    ClearDisplay();

                    // ReadMouse
                    int x = System.Windows.Forms.Control.MousePosition.X - sampleFramework.WindowForm.Left;
                    int y = System.Windows.Forms.Control.MousePosition.Y - sampleFramework.WindowForm.Top;

                    // MiniStates
                    // Play =  1
                    // Stop =  0
                    // Pause = 2
                    // Save =  3

                    if (MiniState > 0)
                    {
                        for (int ch = 0; ch < Timeline.Length; ch++)
                        {
                            Timeline[ch].thisframe = thisFrame;
                            ChannelSchedule = Timeline[ch].ActiveSchedule;
                            ChannelCombo = Timeline[ch].GetActiveCombo(thisFrame);
                            if (ChannelCombo > -1)
                            {
                                RenderChannel(ch, ChannelCombo, ChannelSchedule);
                                if (Timeline[ch].DXComboLib.Count > 0)
                                {
                                    thisCombo = Timeline[ch].DXComboLib[0].ComboName;
                                    thisDuration = Timeline[ch].DXComboLib[0].EndFrame - Timeline[ch].DXComboLib[0].StartFrame;
                                }
                            }
                        }
                    }

                    device.Clear(ClearFlags.Target, System.Drawing.Color.FromArgb(0, 50, 50, 50), 0.0f, 0);
                    device.BeginScene();

                    // Render all display areas to the back buffer                 
                    if (SaveFrame != true)
                    {
                        mSprite.Begin(SpriteFlags.AlphaBlend);
                        foreach (CDisplay display in Display)
                        {
                            mSprite.Transform = Matrix.Scaling(1.0f / Reduction, 1.0f / Reduction, 1.0f) * Matrix.Translation(display.OutPosX / Reduction, display.OutPosY / Reduction, 0.0f);
                            mSprite.Draw(display.m_Texture, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White.ToArgb());
                        }
                        mSprite.End();
                    }
                    else
                    {
                        mSprite.Begin(SpriteFlags.AlphaBlend);
                        foreach (CDisplay display in Display)
                        {
                            mSprite.Transform = Matrix.Scaling(1.0f, 1.0f, 1.0f) * Matrix.Translation(display.OutPosX - (5 * (x - 100)), display.OutPosY - (5 * (y - 100)), 0.0f);
                            mSprite.Draw(display.m_Texture, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White.ToArgb());
                        }
                        mSprite.End();
                        SaveFrameToBmp("ComboName_Frame_" + thisFrame.ToString() + ".bmp");
                    }


                    MiniSprite.Begin(SpriteFlags.AlphaBlend);

                    //MiniSprite.Transform = Matrix.Translation(307, 300 + Yoffset, 0.0f);
                    //MiniSprite.Draw(MiniBoxes, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);


                    //MiniSprite.Transform = Matrix.Translation(110, 215 + Yoffset, 0.0f);
                    MiniSprite.Transform = Matrix.Translation(110, Yoffset, 0.0f);

                    if (y > 50 + Yoffset && y < 102 + Yoffset)
                    {
                        if (x > 418 && x < 475)
                        {
                            MiniSprite.Draw(MiniPlay, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                            if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left)
                            {
                                if (MiniState == 0) thisFrame = 0;
                                MiniState = 1;
                            }
                        }
                        else if (x > 364 && x < 404)
                        {
                            MiniSprite.Draw(MiniPause, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                            if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left)
                                MiniState = 2;

                        }
                        else if (x > 273 && x < 330)
                        {
                            MiniSprite.Draw(MiniStop, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                            if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left)
                                MiniState = 0;
                        }
                        else if (x > 128 && x < 186)
                        {
                            MiniSprite.Draw(MiniRew, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                            if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left && MiniState > 0)
                                thisFrame -= 3;

                        }
                        else if (x > 636 && x < 694)
                        {
                            MiniSprite.Draw(MiniFF, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                            if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left && MiniState > 0)
                                thisFrame += 2;
                        }
                        else if (x > 490 && x < 548)
                        {
                            MiniSprite.Draw(MiniSave, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                            if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left && MiniState > 0)
                                ShowCamera = true;
                        }
                        else if (x > 202 && x < 259)
                        {
                            MiniSprite.Draw(MiniPrev, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                            if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left && MiniState > 0)
                                thisFrame -= 2;
                        }
                        else if (x > 566 && x < 621)
                        {
                            MiniSprite.Draw(MiniNext, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                            if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left && MiniState > 0)
                                thisFrame++;
                        }
                        else
                        {
                            MiniSprite.Draw(MiniBar, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                        }
                    }
                    else
                    {
                        MiniSprite.Draw(MiniBar, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                    }

                    if (MiniState == 1)
                        MiniSprite.Draw(MiniPlaying, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);

                    if (ShowCamera == true)
                    {
                        MiniSprite.Transform = Matrix.Translation(x - 100, y - 100, 0.0f);
                        MiniSprite.Draw(MiniCamera, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                        if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Right)
                            SaveFrame = true;
                    }

                    MiniMessages();
                    MiniSprite.End();

                    textSprite.Begin(SpriteFlags.AlphaBlend);
                    statsFont.DrawText(textSprite, "Display Information: ", 825, Yoffset, System.Drawing.Color.White);
                    statsFont.DrawText(textSprite, String.Format("Frame Rate: {0:0.0} fps", sampleFramework.FPS), 1000, Yoffset, System.Drawing.Color.DarkGray);
                    statsFont.DrawText(textSprite, "Time: " + Expression.FrameToTime(thisFrame, 60) + " - [Frame: " + thisFrame.ToString() + "]", 1000,  30+Yoffset, System.Drawing.Color.DarkGray);
                    statsFont.DrawText(textSprite, "Channel: Ribbons/Tickers", 1000, 50 + Yoffset, System.Drawing.Color.DarkGray);
                    statsFont.DrawText(textSprite, "Requiered Fonts: ", 1220, Yoffset, System.Drawing.Color.White);
                    for(int f = 1; f < DXFontLib.Count; f++)
                        statsFont.DrawText(textSprite, DXFontLib[f].Description.FaceName.Replace("\0", ""), 1300, 20 + (f * 18), System.Drawing.Color.DarkGray);
                    statsFont.DrawText(textSprite, "Options: ", 1220, 50 + Yoffset, System.Drawing.Color.White);
                    statsFont.DrawText(textSprite, "F5 - Configure Display ", 1300, 60 + Yoffset, System.Drawing.Color.DarkGray);
                    textSprite.End();

                    if (MiniState == 1)
                        thisFrame++;
                    device.EndScene();
                }
            }
            catch
            {

            }
        }

        /// <summary>
        /// This function saves the entire content of a frame into a bitmap file
        /// </summary>
        /// <param name="FileName">The desired filename for the BMP</param>
        public void SaveFrameToBmp(string FileName)
        {
            try
            {
                Surface backbuffer = d3d.GetBackBuffer(0, 0, BackBufferType.Mono);
                SurfaceLoader.Save(FileName, ImageFileFormat.Bmp, backbuffer);

                SaveFrame = false;
                ShowCamera = false;

                NotifyNewImage = 300;
                OpenWithFirefox(FileName);
            }
            catch
            {
                //System.Windows.Forms.MessageBox.Show("Error Saving Screenshot");
            }
        }

        /// <summary>
        /// This function opens a file with Firefox, or if Firefox fails, with internet explorer
        /// used to preview the bitmaps saved with SaveFrameToBMP function
        /// </summary>
        /// <param name="FileName">Filename of the file to open</param>
        public void OpenWithFirefox(string FileName)
        {
            try
            {
                Process FireFox = new Process();
                FireFox.StartInfo.FileName = ("Firefox.exe");
                FireFox.StartInfo.Arguments = (@FileName);
                FireFox.Start();
            }
            catch
            {
                Process Explorer = new Process();
                Explorer.StartInfo.FileName = ("IExplore.exe");
                Explorer.StartInfo.Arguments = (@FileName);
                Explorer.Start();
            }
        }

        private int NotifyNewImage = 0;
        private int NotifyMissingMedia = 0;
        private int NotifyLag = 0;
        /// <summary>
        /// This function displays alert messages in the preview window. The duration of the message
        /// is given in frames. For example, by changing the value of int NotifyNewImage to 120, the message
        /// New Imaged loaded will be displayed for 120 frames (2 seconds)
        /// </summary>
        public void MiniMessages()
        {
            int X = 640;
            if (NotifyNewImage > 0)
            {
                MiniSprite.Transform = Matrix.Scaling(0.4f, 0.4f, 0.0f) * Matrix.Translation(X, 240, 0.0f);
                MiniSprite.Draw(MiniDefault, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                X -= 60;
                NotifyNewImage--;
            }
            if (NotifyLag > 0)
            {
                MiniSprite.Draw(MiniDefault, System.Drawing.Rectangle.Empty, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White);
                MiniSprite.Transform = Matrix.Translation(X, 240, 0.0f);
                X -= 60;
                NotifyLag--;
            }

        }

        private StringBuilder HoldingText = new StringBuilder();
        private int ChannelSchedule = 0;
        private int ChannelCombo = 0;
        private DateTime FrameNow;
        private bool wasSync;
        private bool isHolding;
        private bool isOverride;
        private int CicleCount = 0;
        private bool beginSceneCalled = false;

        private int testCounter = 0;

        // Logging Values
        private double fullframe;
        private double segment;
        StringBuilder FrameLog = new StringBuilder();
        /// <summary>
        /// This callback function will be called at the end of every frame to perform all the 
        /// rendering calls for the scene, and it will also be called if the window needs to be 
        /// repainted. After this function has returned, the sample framework will call 
        /// Device.Present to display the contents of the next buffer in the swap chain
        /// </summary>
        public void Render(Device device, double appTime, float elapsedTime)
        {
            fullframe = Watch1.Tic();
            if (fullframe > 25 && fullscreen == true)
                Track.LogInfo("WARNING: Frame took " + fullframe.ToString());

            if (fullmode != true)
            {
                MiniMe(device);
                return;
            }

            Watch2.Tic();
            UpdateApplication();
            segment = Watch2.Tic();
            if (segment > 5) Track.LogInfo("Time Monitor: UpdateApplication() took " + segment.ToString());



            lock (lock_RenderFunction)
            {
                try
                {
                    // Render All Channels
                    if (Timeline != null)
                    {
                        Timeline[0].SyncSCS = false;
                        FrameNow = Watch1.GetRealTime();
                        foreach (CTimeline TT in Timeline)
                            wasSync = TT.Sync(FrameNow);
                        segment = Watch2.Tic();
                        if (segment > 5) Track.LogInfo("Time Monitor: Sync() took " + segment.ToString());


                        ClearDisplay();
                        segment = Watch2.Tic();
                        if (segment > 5) Track.LogInfo("Time Monitor: ClearDisplay() took " + segment.ToString());

                        for (int ch = 0; ch < Timeline.Length; ch++)
                        {
                            if (Timeline[ch].ChannelID != 4)
                            {
                                Timeline[ch].ActiveCombo = Timeline[ch].GetActiveCombo(Timeline[ch].thisframe);
                                if (Timeline[ch].ActiveCombo > -1)
                                    RenderChannel(ch, Timeline[ch].ActiveCombo, Timeline[ch].ActiveSchedule);
                            }
                            else
                            {
                                if (Timeline[ch].ChannelID == 4 && Timeline[ch].thisframe > 108000)
                                {
                                    SurfaceHelper.BeginScene(Override.m_Surface);
                                    d3d.Clear(ClearFlags.Target, System.Drawing.Color.Black, 1.0f, 1);
                                    Timeline[ch].ActiveCombo = Timeline[ch].GetActiveCombo(Timeline[ch].thisframe);
                                    if (Timeline[ch].ActiveCombo > -1)
                                        OverrideChannel(ch, Timeline[ch].ActiveCombo, Timeline[ch].ActiveSchedule);
                                    SurfaceHelper.EndScene(Filter.None);
                                    isOverride = true;
                                }
                            }

                        }
                        segment = Watch2.Tic();
                        if (segment > 5) Track.LogInfo("Time Monitor: RenderChannel() took " + segment.ToString());

                        //TimeDump.Add(String.Format("Now:,{0:hh:mm:ss:fff},SyncTime,{1:hh:mm:ss:fff},LocalFrame,{2},SyncFrame,{3},Offset(new),{4},Sync,{5}, TimeDiff,{6},TotalFrame,{7}",
                        //            FrameNow, Timeline[0].SyncTime, Timeline[0].thisframe, Timeline[0].SyncFrame, Timeline[0].FrameOffset, wasSync, Timeline[0].SyncDiff.TotalMilliseconds, fullframe));

                    }

                    // RENDER MAIN SCENE
                    // Clear the render target
                    if (sampleFramework.LogMode == "log")
                        device.Clear(ClearFlags.Target, System.Drawing.Color.CornflowerBlue, 1.0f, 1);
                    else
                        device.Clear(ClearFlags.Target, System.Drawing.Color.Black, 1.0f, 1);

                    segment = Watch2.Tic();
                    if (segment > 5) Track.LogInfo("Time Monitor: Device.Clear() took " + segment.ToString());

                    device.BeginScene();
                    beginSceneCalled = true;

                    // Render all display areas to the back buffer
                    if (Timeline != null)
                    {
                        mSprite.Begin(SpriteFlags.None);
                        for (int d = 0; d < Display.Length; d++)
                        {
                            mSprite.Transform = Matrix.Scaling(1.0f / ScaleFactor, 1.0f / ScaleFactor, 1.0f) * Matrix.Translation(Display[d].OutPosX, Display[d].OutPosY, 0.0f);
                            mSprite.Draw(Display[d].m_Texture, Vector3.Empty, Vector3.Empty, 16777215);
                        }
                        if (isOverride == true)
                        {
                            mSprite.Transform = Matrix.Scaling(1.0f / ScaleFactor, 1.0f / ScaleFactor, 1.0f) * Matrix.Translation(Override.OutPosX, Override.OutPosY, 0.0f);
                            mSprite.Draw(Override.m_Texture, Vector3.Empty, Vector3.Empty, 16777215);
                        }
                        mSprite.End();
                    }



                    segment = Watch2.Tic();
                    if (segment > 5) Track.LogInfo("Time Monitor: Render All Surfaces took " + segment.ToString());

                    RenderLog();
                    segment = Watch2.Tic();
                    if (segment > 5) Track.LogInfo("Time Monitor: RenderLog() took " + segment.ToString());

                }
                catch (DirectXException ex)
                {
                    Track.LogInfo(ex.Message);
                }
                if (beginSceneCalled)
                {
                    thisFrame++;
                    beginSceneCalled = false;
                    device.EndScene();
                }
            }

            //when InVBlank==true, raster position is hanging out offscreen at top of the screen, ScanLine==0.
            //make sure raster is drawing the screen, not in blanking. Odds are, InVBlank==false right now anyway.

            //while (device.GetRasterStatus(0).InVBlank)
            //{ //just spin. Don't sleep - if we chew up 100% of one of the processor cores, that's ok.

            //    //Watch1.NeedCalibration = true;
            //    //Watch2.NeedCalibration = true;
            //    //Watch3.NeedCalibration = true;
            //};

            ////if (Watch1.NeedCalibration)
            ////    Watch1.Calibrate();
            ////if (Watch2.NeedCalibration)
            ////    Watch2.Calibrate();
            ////if (Watch3.NeedCalibration)
            ////    Watch3.Calibrate();

            ////Wait for raster to finish drawing the current frame, then we'll let this Render() function return and copy this last frame to the screen:

            //while (!device.GetRasterStatus(0).InVBlank)
            //{ //just spin. Don't sleep - if we chew up 100% of one of the processor cores, that's ok.

            //    //Watch1.NeedCalibration = false;
            //    //Watch2.NeedCalibration = false;
            //    //Watch3.NeedCalibration = false;
            //};
            LastFrameRenderedAt = DateTime.Now;

        }


        /// <summary>
        /// This function renders a log of activity on the screen
        /// if LogMode = "default" there will be not visible log in the screen
        /// if LogMode = "stats" only the sync interval and frame information will be displayed
        /// if LogMode = "log" a full log will be displayed on the screen
        /// </summary>
        public void RenderLog()
        {
            if (sampleFramework.LogMode == "default")
            {
                return;
            }
            else if (sampleFramework.LogMode == "stats")
            {
                if (Timeline != null)
                    Stats = Expression.FrameToTime(Timeline[0].thisframe, 60) + " [" + Timeline[0].FrameOffset.ToString() + "/" + intervalSync.TotalMilliseconds.ToString() + "]";
                else
                    Stats = "Waiting for Config...";

                textSprite.Begin(SpriteFlags.AlphaBlend);
                statsFont.DrawText(textSprite, Stats, 10, 10, System.Drawing.Color.Yellow);
                textSprite.End();

            }
            else if (sampleFramework.LogMode == "log")
            {
                if (Timeline != null)
                    Stats = Expression.FrameToTime(Timeline[0].thisframe, 60) + " [" + Timeline[0].FrameOffset.ToString() + "/" + intervalSync.TotalMilliseconds.ToString() + "]";
                else
                    Stats = "Waiting for Config...";
                textSprite.Begin(SpriteFlags.AlphaBlend);
                statsFont.DrawText(textSprite, Stats, 10, 10, System.Drawing.Color.Yellow);
                Track.RenderLog(statsFont, textSprite, 10, 25);
                textSprite.End();
            }

        }


        /// <summary>
        /// Switches the Active ComboCollection in a Timeline.
        /// This function is called everytime a timeline has been updated to switch between
        /// the active ComboCollection and the new updated ComboCollection before all Render
        /// procedures begin.
        /// </summary>
        public void SwapSchedule()
        {
            if (Timeline != null)
                try
                {
                    for (int channel = 0; channel < Timeline.Length; channel++)
                    {
                        if (Timeline[channel].SwapMe == true)
                        {
                            Timeline[channel].SwapCollection();
                            Timeline[channel].SwapMe = false;
                        }
                    }
                }
                catch
                {
                    Console.WriteLine("Error Swaping The Stupid Schedule");
                }
        }

        /// <summary>
        /// Clears all Display Surfaces to Black.
        /// This Function is called at the begining of the Render function.
        /// </summary>
        public void ClearDisplay()
        {
            try
            {
                if (Display != null)
                {
                    for (int i = 0; i < Display.Length; i++)
                    {
                        SurfaceHelper.BeginScene(Display[i].m_Surface);
                        d3d.Clear(ClearFlags.Target, System.Drawing.Color.Black, 0.0f, 0);
                        SurfaceHelper.EndScene(Filter.None);
                        Display[i].clear = false;
                    }
                }
                else
                {
                    Track.LogInfo("Unable to Clear Display (Display = null)");
                }
            }
            catch
            {
                Track.LogInfo("Unable to Clear Display (Exception)");
            }
        }

        List<StringEx> StringCollection = new List<StringEx>();
        /// <summary>
        /// Draws data (text and images) to the respective Channels.
        /// </summary>
        /// <param name="channel">ID of the channel to be rendered</param>
        /// <param name="cmb">ID of the first combo alive in the channel's timeline</param>
        /// <param name="col">ID of the active collection in the channel's timeline</param>
        public void RenderChannel(int channel, int cmb, int col)
        {
            int lastCall = 200;
            try
            {
                // First, Reconstruct the complex strings within the visible horizontal
                // range of the channel display
                RebuildString(StringCollection, channel, col, cmb);

                // For each string returned, follow the rendering steps
                foreach (StringEx cs in StringCollection)
                {
                    for (int display = 0; display < Display.Length; display++)
                        if (Display[display].Channel == Timeline[channel].ChannelID)
                        {
                            SurfaceHelper.BeginScene(Display[display].m_Surface);
                            if (cs.String.Contains("[BGCOLOR]"))
                            {
                                lastCall = 201;
                                if (Display[display].ContainsPoint(cs.Position.Y + 100))
                                {
                                    d3d.Clear(ClearFlags.Target, cs.bgcolor, 0.0f, 0);
                                    Display[display].clear = true;
                                }
                            }

                            if (DXFontLib.Count > cs.FontID && cs.FontID >= 0)
                                if (isVisible(display, Display[display].OffsetX, cs.Position.Y + DXFontLib[cs.FontID].Description.Height, Display[display].Width, DXFontLib[cs.FontID].Description.Height))
                                {
                                    lastCall = 202;
                                    if (cs.String.Contains("[IMAGETAG ID"))
                                    {
                                        lastCall = 203;
                                        int imageid = LocateImage(Expression.GetIntValue(cs.String));
                                        float imagecenter = cs.Position.Y + (DXImageLib[imageid].Height / 2);
                                        if (imagecenter > Display[display].OffsetY && imagecenter < (Display[display].OffsetY + Display[display].Height))
                                        {
                                            RenderImage(imageid, cs.Position.X - Display[display].OffsetX, Convert.ToInt32((cs.Position.Y - Display[display].OffsetY) + (DXFontLib[cs.FontID].Description.Height - DXImageLib[imageid].Height)));
                                        }
                                    }
                                    else if (cs.String.Contains("[CC1]") || cs.String.Contains("[cc1]"))
                                    {
                                        lastCall = 204;
                                        Display[display].m_Sprite.Begin(SpriteFlags.AlphaBlend);
                                        CloseCaption1.Draw(DXFontLib[1], BlackBox, cs.Color, 3, 140, cs.Position.X - Display[display].OffsetX, cs.Position.Y - Display[display].OffsetY);
                                        Display[display].m_Sprite.End();
                                    }
                                    else if (cs.String.Contains("[CC2]") || cs.String.Contains("[cc2]"))
                                    {
                                        lastCall = 205;
                                        Display[display].m_Sprite.Begin(SpriteFlags.AlphaBlend);
                                        CloseCaption2.Draw(DXFontLib[1], BlackBox, cs.Color, 3, 140, cs.Position.X - Display[display].OffsetX, cs.Position.Y - Display[display].OffsetY);
                                        Display[display].m_Sprite.End();
                                    }
                                    else
                                    {
                                        int Descent = DXFontDesc[cs.FontID];

                                        lastCall = 206;
                                        Display[display].m_Sprite.Begin(SpriteFlags.AlphaBlend);
                                        if (cs.String != "[BGCOLOR]")
                                        {
                                            lastCall = 208;
                                            DXFontLib[cs.FontID].DrawText(Display[display].m_Sprite, ResolveString(cs.String), cs.Position.X - Display[display].OffsetX, (cs.Position.Y + Descent) - Display[display].OffsetY, cs.Color);
                                        }

                                        Display[display].m_Sprite.End();
                                    }

                                    if (Display[display].cover_h > 0)
                                        CoverRibbon(Display[display].cover_x, Display[display].cover_y, Display[display].cover_w, Display[display].cover_h, true);

                                }
                            lastCall = 209;
                            SurfaceHelper.EndScene(Filter.None);
                        }
                }
            }
            catch
            {
                Track.LogInfo("Error Rendering Channel (Exception + " + lastCall.ToString() + ")");
                SurfaceHelper.EndScene(Filter.None);
            }
        }


        /// <summary>
        /// This function is a version of the RenderChannel function destinated to override the content
        /// being draw in the seventh ribbon (or any other channel that is being overriden)
        /// </summary>
        /// <param name="channel">The channel to override (usually 4)</param>
        /// <param name="cmb">The Combo</param>
        /// <param name="col">The active Schedule</param>
        public void OverrideChannel(int channel, int cmb, int col)
        {
            try
            {
                RebuildString(StringCollection, channel, col, cmb);
                if (StringCollection.Count > 0)
                {
                    foreach (StringEx cs in StringCollection)
                    {
                        if (cs.String.Contains("[BGCOLOR]"))
                        {
                            if (Override.ContainsPoint(cs.Position.Y + 100))
                            {
                                d3d.Clear(ClearFlags.Target, cs.bgcolor, 0.0f, 0);
                                Override.clear = true;
                                continue;
                            }
                        }

                        if (cs.String.Contains("[IMAGETAG ID"))
                        {
                            //Render as Image, using DXImageLib
                            RenderImage(LocateImage(Expression.GetIntValue(cs.String)), cs.Position.X - Override.OffsetX, cs.Position.Y - Override.OffsetY);
                        }
                        else
                        {
                            // if the string represents a data tag, replace its string value for the corresponding tag value
                            int Descent = DXFontDesc[cs.FontID];
                            Override.m_Sprite.Begin(SpriteFlags.AlphaBlend);
                            DXFontLib[cs.FontID].DrawText(Override.m_Sprite, ResolveString(cs.String), cs.Position.X - Override.OffsetX, (cs.Position.Y + Descent) - Override.OffsetY, cs.Color);
                            Override.m_Sprite.End();
                        }
                    }
                }
            }
            catch
            {
                Track.LogInfo("Error Rendering 7th Ribbon Channel()");
            }
        }

        private string TagCapitalized;
        /// <summary>
        /// the ResolveString function is called by the RenderChannel function. if the string passed as an argument
        /// corresponds to a data tag, image or another pre coded value, the ResolveString function returns the
        /// correct value for the tag, if the string corresponds to pure text, the function returns the same string
        /// this function supports DateTime Tags and Countdown Tags
        /// </summary>
        /// <param name="DataTag"></param>
        /// <returns></returns>
        public string ResolveString(string DataTag)
        {
            try
            {
                TagCapitalized = DataTag.ToUpper();

                //if (TagCapitalized.Contains("[EL"))
                //{
                //    DataRow[] drr = m_Ds.Tables["DATATAG"].Select("Name=" + DataTag);
                //    return drr[0]["LastValue"].ToString();
                //    //return null;
                //}
                //else
                //{
                    TagCapitalized = DataTag.ToUpper();


                    if (TagCapitalized.Contains("[DATATAG"))
                    {
                        if (TagCapitalized == "[DATATAG LIVETIME24HM]")
                            return DateTime.Now.ToString("hh:mm");
                        if (TagCapitalized == "[DATATAG LIVETIME24HMS]")
                            return DateTime.Now.ToString("hh:mm:ss");
                        if (TagCapitalized == "[DATATAG LIVETIMEAPHM]")
                            return DateTime.Now.ToString("hh:mm tt");
                        if (TagCapitalized == "[DATATAG LIVETIMEAPHMS]")
                            return DateTime.Now.ToString("hh:mm:ss tt");
                    }
                    if (TagCapitalized.Contains("[COUNTDOWN"))
                    {
                        TimeSpan CountDown;
                        if (TagCapitalized == "[COUNTDOWN MIDNIGHT]")
                        {
                            int frames = (60 * 60 * 60 * 24 + 60) - (Timeline[0].thisframe + Timeline[0].RTDiff);
                            if (frames > (60 * 60 * 60 * 24 - 60 * 60 * 10))
                                frames = 0;
                            return Expression.FrameToTime(frames, 60);
                        }
                        else if (TagCapitalized == "[COUNTDOWN NOW]")
                        {
                            //int lastDot;

                            CountDown = EndClock - DateTime.Now;
                            if (CountDown.TotalSeconds > 0)
                            {
                                string down = CountDown.TotalHours.ToString();
                                down = down.Remove(down.IndexOf('.'));
                                int hr = Convert.ToInt32(down);
                                int min = CountDown.Minutes;
                                int sec = CountDown.Seconds;

                                string hrs;
                                string mins;
                                string secs;

                                if (hr < 100 && hr >= 10)
                                    hrs = "0" + hr;
                                else if (hr < 10)
                                    hrs = "00" + hr;
                                else
                                    hrs = hr.ToString();

                                if (min < 10)
                                    mins = "0" + min;
                                else
                                    mins = min.ToString();

                                if (sec < 10)
                                    secs = "0" + sec;
                                else
                                    secs = sec.ToString();

                                return mins + ":" + secs;

                                //lastDot = CountDown.ToString().LastIndexOf('.');

                                //if (lastDot > 0)
                                //    return CountDown.ToString().Remove(lastDot);
                                //else
                                //    return CountDown.ToString();
                            }
                            else
                                return "00:00";
                        }
                        else
                        {
                            CountDown = DateTime.Parse("1/1/10 00:00:00") - DateTime.Now;
                            return ((int)CountDown.TotalDays).ToString();
                        }
                    }

                    return DataTag;
                //}
            }
            catch
            {
                Track.LogInfo("Exception was Thrown at ResolveString()");
                return DataTag;
            }
        }


        /// <summary>
        /// This function renders an image in the channel alinging it with the baseline of the Text,
        /// the image is located by its ID number given by the show control system.
        /// If the image is 100 pixels high, the image will be positioned to entirely cover the ribbon.
        /// </summary>
        /// <param name="ImageID">The ID number of the image (not its index in the array)</param>
        /// <param name="X">The X position of the image</param>
        /// <param name="Y">The Y position of the image</param>
        public void RenderImage(int ImageID, int X, int Y)
        {
            try
            {
                if (fullmode == true)
                {
                    if (ImageID >= 0 && DXImageLib[ImageID].Image != null)
                    {
                        if (DXImageLib[ImageID].Height != 100 * ScaleFactor)
                        {
                            DXImageLib[ImageID].XSprite.Begin(SpriteFlags.AlphaBlend);
                            DXImageLib[ImageID].XSprite.Transform = Matrix.Scaling(1.0f * ScaleFactor, 1.0f * ScaleFactor, 0.0f) * Matrix.Translation(X, Y + (2 * ScaleFactor), 0.0f);
                            DXImageLib[ImageID].XSprite.Draw(DXImageLib[ImageID].Image, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White.ToArgb());
                            DXImageLib[ImageID].XSprite.End();
                        }
                        else
                        {
                            DXImageLib[ImageID].XSprite.Begin(SpriteFlags.AlphaBlend);
                            DXImageLib[ImageID].XSprite.Transform = Matrix.Scaling(1.0f * ScaleFactor, 1.0f * ScaleFactor, 0.0f) * Matrix.Translation(X, 0.0f, 0.0f);
                            DXImageLib[ImageID].XSprite.Draw(DXImageLib[ImageID].Image, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White.ToArgb());
                            DXImageLib[ImageID].XSprite.End();
                        }
                    }
                }
                else
                {
                    MiniSprite.Begin(SpriteFlags.AlphaBlend);
                    MiniSprite.Transform = Matrix.Scaling(1.0f * ScaleFactor, 1.0f * ScaleFactor, 0.0f) * Matrix.Translation(X, Y+50, 0.0f);
                    MiniSprite.Draw(DXImageLib[ImageID].Image, Vector3.Empty, Vector3.Empty, System.Drawing.Color.White.ToArgb());
                    MiniSprite.End();
                }
            }
            catch
            {
            }
        }

        /// <summary>
        ///  This function locates an image in the Image library (DXImageLib) using its ID number
        /// </summary>
        /// <param name="value">The Image's ID</param>
        /// <returns>Returns the Index of the image in the DXImageLib</returns>
        public int LocateImage(int value)
        {
            for (int x = 0; x < DXImageLib.Count; x++)
            {
                if (DXImageLib[x].ID == value)
                    return x;
            }
            return -1;
        }

        private Sprite BlackSprite;
        private Texture BlackTexture;
        /// <summary>
        /// This image covers a ribbon or surface with a black box (Reveal effect)
        /// </summary>
        /// <param name="x">x value of the top left corner of the box</param>
        /// <param name="y">y value of the top left corner of the box</param>
        /// <param name="w">width of the box</param>
        /// <param name="h">height of the box</param>
        /// <param name="front">z position of the box</param>
        public void CoverRibbon(float x, float y, float w, float h, bool front)
        {
            try
            {
                BlackSprite.Begin(SpriteFlags.None);
                BlackSprite.Transform = Matrix.Scaling(w, h, 1.0f) * Matrix.Translation(x, y, 0.0f);
                BlackSprite.Draw(BlackTexture, Vector3.Empty, Vector3.Empty, 16777215);
                BlackSprite.End();
            }
            catch
            {
                Track.LogInfo("Error Covering Ribbon()");
            }
        }

        public StringEx newString;
        public List<StringEx> AllStrings = new List<StringEx>();
        public StringBuilder StrBuilder = new StringBuilder(100);
        /// <summary>
        /// Rebuild Complex String from the Combo's element array.
        /// This function rebuilds the most adecuate string from the fragments in which the initial
        /// TextString was originally broken. The function attempts to reconstruct only the part of the total
        /// string that is visible on the screen during the present frame.
        /// </summary>
        /// <param name="ch">ID for the Channel/Timeline</param>
        /// <param name="col">ID for the Active Collection in the Timeline</param>
        /// <param name="cmb">ID for the Current Combo in the Timeline</param>
        /// <returns></returns>
        private void RebuildString(List<StringEx> AllStrings, int ch, int col, int cmb)
        {
            try
            {
                AllStrings.Clear();
                newString.Clear();
                StrBuilder.Length = 0;
                HoldingText.Length = 0;
                bool WaitFor0 = false;
                if (Timeline[ch].Schedule[col].SchCombo[cmb].ComboIndex < 0)
                    return;
                if (Timeline[ch].DXComboLib.Count < 1)
                    return;

                Combo myCombo = Timeline[ch].DXComboLib[Timeline[ch].Schedule[col].SchCombo[cmb].ComboIndex - 1];
                for (int e = 0; e < myCombo.Fragment.Length; e++)
                {

                    if (myCombo.Fragment[e].hierarchy == 0)
                    {
                        // Save the current contents of the Complex String
                        if (StrBuilder.Length > 0)
                        {
                            newString.String = StrBuilder.ToString();
                            AllStrings.Add(new StringEx(newString));
                            StrBuilder.Length = 0;
                            newString.Clear();
                        }
                        myCombo.Fragment[e].lastPosition = GetPosition(ch, col, cmb, e, Timeline[ch].thisframe);
                        if (myCombo.Fragment[e].BgColor != null && myCombo.Fragment[e].BgColor != System.Drawing.Color.Black)
                        {
                            // Create a ComplexString for the BGColor Tag
                            newString.Color = myCombo.Fragment[e].FontColor;
                            newString.FontID = myCombo.Fragment[e].FontID;
                            newString.bgcolor = myCombo.Fragment[e].BgColor;
                            newString.Position = myCombo.Fragment[e].lastPosition;
                            newString.String = "[BGCOLOR]";
                            // Dump the ComplexString to the Array
                            AllStrings.Add(new StringEx(newString));
                            StrBuilder.Length = 0;
                            newString.Clear();
                        }
                        WaitFor0 = false;
                    }
                    else
                    {
                        if (WaitFor0 == true) continue;
                        myCombo.Fragment[e].lastPosition.X = myCombo.Fragment[e - 1].lastPosition.X + myCombo.Fragment[e - 1].stringwidth;
                        myCombo.Fragment[e].lastPosition.Y = myCombo.Fragment[e - 1].lastPosition.Y;
                        myCombo.Fragment[e].isHolding = myCombo.Fragment[e - 1].isHolding;
                    }

                    if (isFragmentVisible(myCombo.Fragment[e]) == true)
                    {
                        if (myCombo.Fragment[e].stringwidth > 0)
                        {
                            // HANDLE DATATAGS
                            if (Expression.isDataTag(myCombo.Fragment[e].m_string) == true)
                            {
                                // Save the current contents of the Complex String
                                if (StrBuilder.Length > 0)
                                {
                                    newString.String = StrBuilder.ToString();
                                    AllStrings.Add(new StringEx(newString));
                                    StrBuilder.Length = 0;
                                    newString.Clear();
                                }
                                // Create a ComplexString for the DataTag
                                newString.Color = myCombo.Fragment[e].FontColor;
                                newString.FontID = myCombo.Fragment[e].FontID;
                                newString.bgcolor = myCombo.Fragment[e].BgColor;
                                newString.Position = myCombo.Fragment[e].lastPosition;
                                newString.String = myCombo.Fragment[e].m_string;
                                // Dump the ComplexString to the Array
                                AllStrings.Add(new StringEx(newString));
                                StrBuilder.Length = 0;
                                newString.Clear();
                                continue;
                            }

                            // HANDLE TEXT
                            // Check if the string contains the same characteristics than 
                            // the past string and that its hierarchy is not 0, and if its so, append it.
                            if (newString.Compare(myCombo.Fragment[e]) == true)
                            {
                                StrBuilder.Append(myCombo.Fragment[e].m_string);
                                if (myCombo.Fragment[e].isHolding)
                                    HoldingText.Append(myCombo.Fragment[e].m_string);
                                continue;
                            }
                            else
                            {
                                // Save the current contents of the Complex String
                                if (StrBuilder.Length > 0)
                                {
                                    newString.String = StrBuilder.ToString();
                                    AllStrings.Add(new StringEx(newString));
                                    StrBuilder.Length = 0;
                                    newString.Clear();
                                }

                                // Create a new ComplexString
                                newString.Color = myCombo.Fragment[e].FontColor;
                                newString.FontID = myCombo.Fragment[e].FontID;
                                newString.bgcolor = myCombo.Fragment[e].BgColor;
                                newString.Position = myCombo.Fragment[e].lastPosition;
                                StrBuilder.Append(myCombo.Fragment[e].m_string);
                                if (myCombo.Fragment[e].isHolding)
                                    HoldingText.Append(myCombo.Fragment[e].m_string);
                                continue;
                            }
                        }

                    }
                    else
                    {
                        if (myCombo.Fragment[e].lastPosition.X > (Display[0].OffsetX + Display[0].Width))
                        {
                            newString.String = StrBuilder.ToString();
                            AllStrings.Add(new StringEx(newString));
                            StrBuilder.Length = 0;
                            newString.Clear();
                            WaitFor0 = true;
                        }
                    }
                }

                // Last chance to save the current contents of the Complex String
                if (StrBuilder.Length > 0)
                {
                    newString.String = StrBuilder.ToString();
                    AllStrings.Add(new StringEx(newString));
                    StrBuilder.Length = 0;
                    newString.Clear();
                }
            }
            catch
            {
                Track.LogInfo("Error Rebuilding Complex String()");
            }
        }

        /// <summary>
        /// This function determines if a fragment of text is visible in the screen given its 
        /// last position and using the width of the display[0]
        /// </summary>
        /// <param name="fragment">the fragment's id number</param>
        /// <returns></returns>
        private bool isFragmentVisible(Element fragment)
        {
            if (fragment.Visible == true)
                if (fragment.lastPosition.X < (Display[0].OffsetX + Display[0].Width))
                {
                    if ((fragment.lastPosition.X + fragment.stringwidth) > (Display[0].OffsetX))
                        return true;
                }
            return false;
        }


        /// <summary>
        /// Calculates if a rectangle is visible over a determinated display area.
        /// This function is called by the RebuildStringEx function to calculate if the rectangle
        /// occupied by a text fragment (element) is visible on a display surface 
        /// </summary>
        /// <param name="display">the display area index</param>
        /// <param name="x">left bottom corner x value</param>
        /// <param name="y">left bottom corner y value</param>
        /// <param name="w">width of the rectangle</param>
        /// <param name="h">height of the rectangle</param>
        /// <returns></returns>
        public bool isVisible(int display, int x, int y, int w, int h)
        {
            // check if its outside the top margin
            if (y < Display[display].OffsetY)
                return false;

            // check if its outside the bottom margin
            if ((y - h) > Display[display].OffsetY + Display[display].Height)
                return false;

            // check if is outside the left margin
            if ((x + w) < Display[display].OffsetX)
                return false;

            // check if is outside the right margin
            if (x > (Display[display].OffsetX + Display[display].Width))
                return false;

            // if none of the previous is true, then the text is visible.
            return true;
        }


        private System.Drawing.Point MyPoint = new System.Drawing.Point();
        /// <summary>
        /// Calculates the Position of a TextElement on the display area for a given frame.
        /// This function is called by the RebuildStringEx() function to calculate the position of 
        /// a given text element on a display area for a given frame. These calculations are based on the 
        /// element's "Details" string information.
        /// </summary>
        /// <param name="channel">ID for the Channel/Timeline</param>
        /// <param name="collection">ID for the ComboCollection</param>
        /// <param name="combo">ID for the current Combo in the Timeline</param>
        /// <param name="element">ID for the Element</param>
        /// <param name="frame">The frame to be calculated</param>
        /// <returns></returns>
        public System.Drawing.Point GetPosition(int ch, int sch, int cmb, int elm, int frame)
        {
            try
            {
                float position = 0;
                int ComboID = Timeline[ch].Schedule[sch].SchCombo[cmb].ComboIndex - 1;

                float frames = (Timeline[ch].DXComboLib[ComboID].Fragment[elm].endframe - Timeline[ch].DXComboLib[ComboID].Fragment[elm].startframe); // -(Timeline[ch].DXComboLib[ComboID].Fragment[elm].accelerate / 2);
                float pixels = Timeline[ch].DXComboLib[ComboID].Fragment[elm].endx - Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx;
                int elapsed = (Timeline[ch].thisframe - Timeline[ch].Schedule[sch].SchCombo[cmb].startFrame); //+ Timeline[ch].DXComboLib[ComboID].Fragment[elm].startframe));
                int restante = (Timeline[ch].DXComboLib[ComboID].Fragment[elm].endframe - Timeline[ch].DXComboLib[ComboID].Fragment[elm].startframe) - elapsed;
                int duration = Timeline[ch].DXComboLib[ComboID].Fragment[elm].duration;
                int hierarchy = Timeline[ch].DXComboLib[ComboID].Fragment[elm].hierarchy;

                // CALCULATE Y
                MyPoint.Y = Timeline[ch].DXComboLib[ComboID].Fragment[elm].starty - Timeline[ch].DXComboLib[ComboID].Fragment[elm].FontSize;

                // CALCULATE X
                // Default Calculation, if there are not special parameters
                if (pixels != 0)
                    position = Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx + ((pixels / frames) * elapsed);
                else
                    position = Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx;

                // Constant Speed Calculations
                if (Timeline[ch].DXComboLib[ComboID].Fragment[elm].ConstantSpeedX != 0)
                {
                    int StartX = Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx;
                    float ConstantSpeed = Timeline[ch].DXComboLib[ComboID].Fragment[elm].ConstantSpeedX;
                    position = StartX + (ConstantSpeed * elapsed);
                }

                // Target Speed Calculations 
                if (Timeline[ch].DXComboLib[ComboID].Fragment[elm].TargetSpeed != 0)
                {
                    int StartX = Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx;
                    float TargetSpeed = Timeline[ch].DXComboLib[ComboID].Fragment[elm].TargetSpeed;
                    position = StartX + (TargetSpeed * elapsed);
                }

                // SweepIn Calculations
                if (Timeline[ch].DXComboLib[ComboID].Fragment[elm].SweepIn != null)
                {
                    int Region = 0;
                    int StartX = Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx;
                    float TargetSpeed = Timeline[ch].DXComboLib[ComboID].Fragment[elm].TargetSpeed;
                    if (TargetSpeed == 0.0) TargetSpeed = Timeline[ch].DXComboLib[ComboID].Fragment[elm].ConstantSpeedX;
                    float ConstantSpeed = Timeline[ch].DXComboLib[ComboID].Fragment[elm].ConstantSpeedX;
                    float FramesToSweepIn = Expression.GetIntValue(Timeline[ch].DXComboLib[ComboID].Fragment[elm].SweepIn, 1);
                    float PixelsToSweepIn = Expression.GetIntValue(Timeline[ch].DXComboLib[ComboID].Fragment[elm].SweepIn) * ScaleFactor;
                    float FramesToSweepOut = Expression.GetIntValue(Timeline[ch].DXComboLib[ComboID].Fragment[elm].SweepOut, 1);
                    float PixelsToSweepOut = Expression.GetIntValue(Timeline[ch].DXComboLib[ComboID].Fragment[elm].SweepIn) * ScaleFactor;
                    float SweepInRatio = PixelsToSweepIn / FramesToSweepIn;
                    float SweepOutRatio = PixelsToSweepOut / FramesToSweepOut;

                    if (elapsed > 0 && elapsed <= FramesToSweepIn)
                        Region = 1;
                    else if (elapsed > FramesToSweepIn && elapsed <= (duration - FramesToSweepOut))
                        Region = 2;
                    else if (elapsed > (duration - FramesToSweepOut))
                        Region = 3;

                    if (Region == 1)
                    {
                        // SweepIn
                        position = StartX + (SweepInRatio * elapsed);
                    }
                    else if (Region == 2)
                    {
                        // Regular
                        float FramesInRegion2 = elapsed - FramesToSweepIn;
                        position = StartX + PixelsToSweepIn + (TargetSpeed * FramesInRegion2);
                    }
                    else if (Region == 3)
                    {
                        // SweepOut
                        float FramesInRegion3 = FramesToSweepOut - (duration - elapsed);
                        float FramesInRegion2 = duration - (FramesToSweepIn + FramesToSweepOut);
                        position = StartX + PixelsToSweepIn + (TargetSpeed * FramesInRegion2) + (SweepOutRatio * FramesInRegion3);
                    }
                    else if (Region == 0)
                    {
                        Region = 5;
                    }
                }

                // Accelerate Calculations
                if (Timeline[ch].DXComboLib[ComboID].Fragment[elm].AccelerateIn != null)
                {
                    float StartX = Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx;
                    float EndX = StartX - Timeline[ch].DXComboLib[ComboID].Fragment[elm].endx;
                    float AccPixels = Expression.GetIntValue(Timeline[ch].DXComboLib[ComboID].Fragment[elm].AccelerateIn);
                    float AccFrames = Expression.GetIntValue(Timeline[ch].DXComboLib[ComboID].Fragment[elm].AccelerateIn, 1);
                    float Pixels = (EndX - StartX) + (AccPixels / 2);
                    int Frames = Timeline[ch].DXComboLib[ComboID].Fragment[elm].endframe - Timeline[ch].DXComboLib[ComboID].Fragment[elm].startframe;
                    float TSpeed = Timeline[ch].DXComboLib[ComboID].Fragment[elm].TargetSpeed;

                    int Region = 0;

                    if (elapsed > 0 && elapsed < AccFrames)
                        Region = 1;
                    if (elapsed >= AccFrames)
                        Region = 2;

                    if (Region == 1)
                    {
                        TSpeed += (TSpeed / AccFrames) * elapsed;
                        position = StartX + (elapsed * TSpeed);
                    }
                    else if (Region == 2)
                    {
                        position = StartX + (elapsed * TSpeed);
                    }
                    else if (Region == 3)
                    {
                        position = (elapsed * TSpeed) + StartX;
                        position += ((TSpeed * 2) / AccPixels) * (AccFrames - elapsed);
                    }
                }

                // HoldPoint Calculations
                else if (Timeline[ch].DXComboLib[ComboID].Fragment[elm - hierarchy].HoldingFrame != null)
                {
                    if (Timeline[ch].DXComboLib[ComboID].Fragment[elm - hierarchy].HoldingFrame.Count > 0)
                    {
                        bool HoldText = true;
                        if (Timeline[ch].ChannelID < 4 && Timeline[ch].thisframe < 1080000)
                            HoldText = false;
                        if (Timeline[ch].ChannelID == 4 && Timeline[ch].thisframe < 216000)
                            HoldText = false;

                        if (HoldText == true || localtest == true)
                        {
                            if (elapsed < 2)
                            {
                                Timeline[ch].DXComboLib[ComboID].Fragment[elm].isHolding = false;
                                Timeline[ch].DXComboLib[ComboID].Fragment[elm].FramesHolded = 0;
                            }

                            int MyFrame = elapsed - Timeline[ch].DXComboLib[ComboID].Fragment[elm].FramesHolded;
                            float MySpeed = Timeline[ch].DXComboLib[ComboID].Fragment[elm].ConstantSpeedX;
                            if (MySpeed == 0.0f) MySpeed = Timeline[ch].DXComboLib[ComboID].Fragment[elm].TargetSpeed;

                            // Check if the fragment is currently being held
                            if (Timeline[ch].DXComboLib[ComboID].Fragment[elm].isHolding == true)
                            {
                                if (Timeline[ch].UnPause <= Timeline[ch].thisframe && Timeline[ch].UnPause != 0)
                                {
                                    Timeline[ch].DXComboLib[ComboID].Fragment[elm].isHolding = false;
                                    position = Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx + Convert.ToInt32(MySpeed * MyFrame);
                                    Timeline[ch].UnPause = 0;
                                }
                                else
                                {
                                    position = Timeline[ch].DXComboLib[ComboID].Fragment[elm].lastPosition.X;
                                    Timeline[ch].DXComboLib[ComboID].Fragment[elm].FramesHolded++;

                                }
                            }
                            else
                            {
                                position = Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx + Convert.ToInt32(MySpeed * MyFrame);
                                for (int f = 0; f < Timeline[ch].DXComboLib[ComboID].Fragment[elm - hierarchy].HoldingFrame.Count; f++)
                                {
                                    if (MyFrame == (int)Timeline[ch].DXComboLib[ComboID].Fragment[elm - hierarchy].HoldingFrame[f])
                                    {
                                        position = Timeline[ch].DXComboLib[ComboID].Fragment[elm].startx + Convert.ToInt32(MySpeed * MyFrame);
                                        Timeline[ch].DXComboLib[ComboID].Fragment[elm].FramesHolded++;
                                        Timeline[ch].DXComboLib[ComboID].Fragment[elm].isHolding = true;
                                        break;
                                    }

                                    // if the current frame is less than the holding frame, keep looking
                                    if (MyFrame < (int)Timeline[ch].DXComboLib[ComboID].Fragment[elm - hierarchy].HoldingFrame[f])
                                        break;
                                }
                            }
                        }
                    }
                }

                // Reveal
                if (Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealIn != null)
                {
                    // locate in which displays the fragment is visible
                    if (elapsed < Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealF)
                        for (int z = 0; z < Display.Length; z++)
                        {
                            if (Display[z].Channel == Timeline[ch].ChannelID)
                            {
                                if (Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealIn.Contains("UP"))
                                {
                                    Display[z].cover_x = Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealX;
                                    Display[z].cover_y = Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealY;
                                    Display[z].cover_w = Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealW;
                                    Display[z].cover_h = Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealH - ((Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealH / Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealF) * elapsed);
                                }
                                else if (Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealIn.Contains("DOWN"))
                                {
                                    Display[z].cover_x = Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealX;
                                    Display[z].cover_y = Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealY + (elapsed * (Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealH / Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealF));
                                    Display[z].cover_w = Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealW;
                                    Display[z].cover_h = Timeline[ch].DXComboLib[ComboID].Fragment[elm].RevealH;
                                }
                            }
                        }
                }

                MyPoint.X = Convert.ToInt32(position);
                return MyPoint;
            }
            catch
            {
                Track.LogInfo("Error Rebuilding Complex String (GetPosition)");
                return new System.Drawing.Point(0, 0);
            }
        }

        private bool isTimeSampling = true;
        /// <summary>
        /// As a convenience, the sample framework inspects the incoming windows messages for
        /// keystroke messages and decodes the message parameters to pass relevant keyboard
        /// messages to the application.  The framework does not remove the underlying keystroke 
        /// messages, which are still passed to the application's MsgProc callback.
        /// </summary>
        private void OnKeyEvent(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            switch (e.KeyCode)
            {

                case System.Windows.Forms.Keys.F9:
                    ReportTimelines();
                    break;
                case System.Windows.Forms.Keys.F10:
                    PrintUpdates = true;
                    break;
                case System.Windows.Forms.Keys.F11:
                    PrintUpdates = false;
                    break;
                case System.Windows.Forms.Keys.F1:
                    isHelpShowing = !isHelpShowing;
                    break;
                case System.Windows.Forms.Keys.R:
                    for (int x = 0; x < Timeline.Length; x++)
                        Timeline[x].thisframe = 0;
                    break;
                case System.Windows.Forms.Keys.U:
                    for (int x = 0; x < Timeline.Length; x++)
                        Timeline[x].UnPause = Timeline[x].thisframe + 60;
                    break;
                case System.Windows.Forms.Keys.Left:
                    for (int x = 0; x < Timeline.Length; x++)
                        Timeline[x].thisframe += 5;
                    break;
                case System.Windows.Forms.Keys.Right:
                    for (int x = 0; x < Timeline.Length; x++)
                        Timeline[x].thisframe -= 5;
                    break;
            }
        }

        /// <summary>
        /// Before handling window messages, the sample framework passes incoming windows 
        /// messages to the application through this callback function. If the application sets 
        /// noFurtherProcessing to true, the sample framework will not process the message
        /// </summary>
        public IntPtr OnMsgProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam, ref bool noFurtherProcessing)
        {
            // Give the dialog a chance to handle the message first
            noFurtherProcessing = hud.MessageProc(hWnd, msg, wParam, lParam);
            if (noFurtherProcessing)
                return IntPtr.Zero;

            noFurtherProcessing = sampleUi.MessageProc(hWnd, msg, wParam, lParam);
            if (noFurtherProcessing)
                return IntPtr.Zero;

            // Pass all remaining windows messages to camera so it can respond to user input
            camera.HandleMessages(hWnd, msg, wParam, lParam);
            return IntPtr.Zero;
        }

        /// <summary>
        /// Initializes the application
        /// </summary>
        public void InitializeApplication()
        {
            int y = 10;
            // Initialize the HUD
            Button fullScreen = hud.AddButton(ToggleFullscreen, "Toggle full screen", 35, y, 125, 22);
            Button toggleRef = hud.AddButton(ToggleReference, "Toggle reference (F3)", 35, y += 24, 125, 22);
            Button changeDevice = hud.AddButton(ChangeDevice, "Change Device (F2)", 35, y += 24, 125, 22);
            // Hook the button events for when these items are clicked
            fullScreen.Click += new EventHandler(OnFullscreenClicked);
            toggleRef.Click += new EventHandler(OnRefClicked);
            changeDevice.Click += new EventHandler(OnChangeDeviceClicked);

            Expression = new CExpressions(true);

            // If you wanted to respond to any of these you would need to add an event hook here
            Watch1.CalibrateToRealTime();
            Watch2.CalibrateToRealTime();
            Watch3.CalibrateToRealTime();
            AddFontToQueue("Arial", 100);
            AddFontToQueue("Arial", 160);

            // IF Image Directory Doesnt Exist, Create it
            if (Directory.Exists("C:\\TextOverlay Images") != true)
                Directory.CreateDirectory("C:\\TextOverlay Images\\");

            if (Directory.Exists("Y:\\") != true)
                ReportToSCS("<Activity ID=\"20034\" Desc=\"Unable to Access Y: Folder\" />");
            if (localtest == false && fullmode == true)
            {
                System.Windows.Forms.Cursor.Position = new System.Drawing.Point(1000, 1005);
                System.Windows.Forms.Cursor.Hide();
            }
           // string test = Expression.FrameToTime(216060, 60);
        }

        private ArrayList testArray = new ArrayList();
        /// <summary>Called when the change device button is clicked</summary>
        private void OnChangeDeviceClicked(object sender, EventArgs e)
        {
            sampleFramework.ShowSettingsDialog(!sampleFramework.IsD3DSettingsDialogShowing);
        }

        /// <summary>Called when the full screen button is clicked</summary>
        private void OnFullscreenClicked(object sender, EventArgs e)
        {
            sampleFramework.ToggleFullscreen();
        }

        /// <summary>Called when the ref button is clicked</summary>
        private void OnRefClicked(object sender, EventArgs e)
        {
            sampleFramework.ToggleReference();
        }


        private Device AuxDevice = null;
        /// <summary>
        /// Creates a second auxiliary Direct3D device to be used for the creation of
        /// measuring fonts. This device is meant to work independently without blocking the
        /// main rendering Direct3D device.
        /// </summary>
        public void CreateAuxDevice()
        {
            try
            {
                System.Windows.Forms.Form f = new System.Windows.Forms.Form();
                PresentParameters pps = new PresentParameters();

                pps.Windowed = true;
                pps.BackBufferCount = 1;
                pps.BackBufferFormat = Format.Unknown;
                pps.BackBufferWidth = 820;
                pps.BackBufferHeight = 400;
                pps.SwapEffect = SwapEffect.Discard;
                pps.ForceNoMultiThreadedFlag = true;
                AuxDevice = new Device(0, DeviceType.Hardware, f, CreateFlags.HardwareVertexProcessing | CreateFlags.MultiThreaded, pps); // Put everything into the device
            }
            catch
            {

            }
        }

        private int thisFrame = 0;
        private static ArrayList TimeDump = new ArrayList();
        private System.Object lock_RenderFunction = new System.Object();
        public XmlDataDocument CurrentXMLConfig;
        private int ScaleFactor = 1;
        private int Reduction = 5;
        private CDisplay[] Display;
        private CDisplay Override;
        private CTimeline[] Timeline;
        private RenderToSurface SurfaceHelper;
        private Sprite mSprite = null;
        private Device d3d = null;
        private List<Font> DXFontLib = new List<Font>();
        private List<int> DXFontDesc = new List<int>();


        public void ReportTimelines()
        {
            for (int ch = 0; ch < Timeline.Length; ch++)
            {
                FileStream file = new FileStream(String.Format("C:/Timeline Channel{0}.html", ch), FileMode.Create, FileAccess.Write);
                StreamWriter sw = new StreamWriter(file);
                sw.WriteLine("<html><PRE>");
                sw.WriteLine(" ");
                sw.WriteLine("Combo Library");
                for (int i = 0; i < Timeline[ch].DXComboLib.Count; i++)
                {
                    sw.WriteLine("Combo " + i.ToString());
                    sw.WriteLine(Timeline[ch].DXComboLib[i].FullXml.Replace("<", "[").Replace(">", "]"));
                    sw.WriteLine(" ");
                }
                sw.WriteLine(" ");
                sw.WriteLine(" ");
                sw.WriteLine("Timeline");
                for (int a = 0; a < 20000; a++)
                {
                    if (Timeline[ch].Schedule[0].SchCombo[a].ComboIndex > 0)
                    {
                        sw.WriteLine("Block " + a.ToString() + " ->" + Expression.FrameToTime(Timeline[ch].Schedule[0].SchCombo[a].startFrame, 60) );
                        sw.WriteLine("  Combo Index: " + Timeline[ch].Schedule[0].SchCombo[a].ComboIndex.ToString());
                        sw.WriteLine("  Combo Start: " + Timeline[ch].Schedule[0].SchCombo[a].startFrame.ToString());
                        sw.WriteLine("  Combo End: " + Timeline[ch].Schedule[0].SchCombo[a].endFrame.ToString());
                        sw.WriteLine(" ");
                    }
                    else
                        break;
                }

                sw.WriteLine("</PRE></html>");
                sw.Close();
                file.Close();
            }
        }


        public void PrintXmlUpdate(string XmlUpdate, string filename)
        {
            FileStream file = new FileStream(String.Format("C:/{0}.txt", filename), FileMode.Create, FileAccess.Write);
            StreamWriter sw = new StreamWriter(file);
            sw.WriteLine(XmlUpdate);
            file.Close();
        }
    }
}
